﻿//-----------------------------------------------------------------------
// <copyright file="CleanCodeSettings.cs" company="None">
//     Copyright (c) 2009-2011
// </copyright>
//-----------------------------------------------------------------------

namespace CleanCode.StyleCopCustomRules
{
    using System.Diagnostics.CodeAnalysis;
    using System.Windows.Forms;
    using StyleCop;

    /// <summary>
    /// UserControl to display the cusomizable properties for Clean Code rules.
    /// </summary>
    public partial class CleanCodeSettings : UserControl, IPropertyControlPage
    {
        /// <summary>
        /// Holds the instance of our cusomized <see cref="CleanCodeRulesAnalyzer"/>.
        /// </summary>
        private readonly CleanCodeRulesAnalyzer analyzer;

        /// <summary>
        /// Holds the <see cref="PropertyControl"/> for StyleCop.
        /// </summary>
        private PropertyControl tabControl;

        /// <summary>
        /// Holds the dirty flag for StyleCop.
        /// </summary>
        private bool dirty;

        /// <summary>
        /// Initializes a new instance of the <see cref="CleanCodeSettings"/> class.
        /// </summary>
        /// <param name="analyzer">The analyzer.</param>
        public CleanCodeSettings(CleanCodeRulesAnalyzer analyzer)
        {
            this.analyzer = analyzer;
            this.InitializeComponent();
        }

        #region IPropertyControlPage Members

        /// <summary>
        /// Gets or sets a value indicating whether this <see cref="CleanCodeSettings"/> is dirty.
        /// </summary>
        /// <value><c>true</c> if dirty; otherwise, <c>false</c>.</value>
        public bool Dirty
        {
            get
            {
                return this.dirty;
            }

            set
            {
                if (this.dirty != value)
                {
                    this.dirty = value;
                    this.tabControl.DirtyChanged();
                }
            }
        }

        /// <summary>
        /// Gets the name of the tab.
        /// </summary>
        /// <value>The name of the tab.</value>
        public string TabName
        {
            get 
            {
                return "Clean Code Settings";
            }
        }

        /// <summary>
        /// Activates the specified activated.
        /// </summary>
        /// <param name="activated">if set to <c>true</c> [activated].</param>
        [SuppressMessage("CleanCode.StyleCopCustomRules.CleanCodeRulesAnalyzer", "CC4603:FlagParameters",
            Justification = "Given API by StyleCop's IPropertyControlPage.")]
        public void Activate(bool activated)
        {
        }

        /// <summary>
        /// Will be called by user request for 'Ok' or 'Apply' and saves the settings persistently.
        /// </summary>
        /// <returns>Returns true if all settings could be saved, false if not.</returns>
        public bool Apply()
        {
            if (this.analyzer != null)
            {
                bool fullResult = false;
                fullResult |= this.SaveParameterLimit();
                fullResult |= this.SaveMehthodLengthLimit();

                if (fullResult == false)
                {
                    return false;
                }
            }

            this.dirty = false;
            this.tabControl.DirtyChanged();

            return true;
        }

        /// <summary>
        /// Initializes the specified property control.
        /// </summary>
        /// <param name="propertyControl">The property control.</param>
        public void Initialize(PropertyControl propertyControl)
        {
            // Save the property control.
            this.tabControl = propertyControl;

            // Load the current settings and initialize the controls on the form.
            this.InitializeSettings();

            // Put the form into 'not-dirty' state.
            this.dirty = false;
            this.tabControl.DirtyChanged();
        }

        /// <summary>
        /// Posts the apply.
        /// </summary>
        /// <param name="wasDirty">if set to <c>true</c> [was dirty].</param>
        [SuppressMessage("CleanCode.StyleCopCustomRules.CleanCodeRulesAnalyzer", "CC4603:FlagParameters", 
            Justification = "Given API by StyleCop's IPropertyControlPage.")]
        public void PostApply(bool wasDirty)
        {
        }

        /// <summary>
        /// Pres the apply.
        /// </summary>
        /// <returns>Returns always true.</returns>
        public bool PreApply()
        {
            return true;
        }

        /// <summary>
        /// Refreshes the state of the settings override.
        /// </summary>
        public void RefreshSettingsOverrideState()
        {
        }

        /// <summary>
        /// Saves the ParameterLimit property persistently.
        /// </summary>
        /// <returns>Returns true if successful, false otherwise.</returns>
        private bool SaveParameterLimit()
        {
            return this.SetIntPropertyValue("ParameterLimit", this.parameterLimitTextBox.Text);
        }

        /// <summary>
        /// Saves the MethodLengthLimit property persistently.
        /// </summary>
        /// <returns>Returns true if successful, false otherwise.</returns>
        private bool SaveMehthodLengthLimit()
        {
            return this.SetIntPropertyValue("MethodLengthLimit", this.methodLengthTextBox.Text);
        }

        /// <summary>
        /// Sets the int property value by a given text.
        /// </summary>
        /// <param name="propertyName">Name of the property to get the value.</param>
        /// <param name="text">The text from the <see cref="TextBox"/> containing the value to parse and set.</param>
        /// <returns>
        /// Returns true if successful, false otherwise.
        /// </returns>
        private bool SetIntPropertyValue(string propertyName, string text)
        {
            if (text == string.Empty)
            {
                this.analyzer.ClearSetting(this.tabControl.LocalSettings, propertyName);
            }
            else
            {
                int value;
                bool parseOk = int.TryParse(text, out value);
                if (parseOk == false)
                {
                    return false;
                }

                this.analyzer.SetSetting(
                    this.tabControl.LocalSettings,
                    new IntProperty(this.analyzer, propertyName, value));
            }

            return true;
        }

        /// <summary>
        /// Initializes the settings.
        /// </summary>
        private void InitializeSettings()
        {
            // Load the current setting of my custom string property.
            this.LoadParameterLimit();
            this.LoadMethodLengthLimit();
        }

        /// <summary>
        /// Loads the ParameterLimit property from the given settings.
        /// </summary>
        private void LoadParameterLimit()
        {
            int value = this.GetIntSettingOrDefaultValue("ParameterLimit");
            this.parameterLimitTextBox.Text = value.ToString();
        }

        /// <summary>
        /// Loads the MethodLengthLimit property from the given settings.
        /// </summary>
        private void LoadMethodLengthLimit()
        {
            int value = this.GetIntSettingOrDefaultValue("MethodLengthLimit");
            this.methodLengthTextBox.Text = value.ToString();
        }

        /// <summary>
        /// Gets the setting for an integer value from the settings
        /// or the corresponding default value if the setting wasn't available.
        /// </summary>
        /// <param name="propertyName">Name of the property to get the value.</param>
        /// <returns>
        /// Returns the integer value of the setting or the default value. 
        /// Returns 0 if the property isn't defined at all.
        /// </returns>
        private int GetIntSettingOrDefaultValue(string propertyName)
        {
            IntProperty property = this.analyzer.GetSetting(
                                       this.tabControl.MergedSettings, propertyName) as IntProperty;

            if (property != null)
            {
                return property.Value;
            }

            var descriptor = this.analyzer.PropertyDescriptors.GetPropertyDescriptor(propertyName) as PropertyDescriptor<int>;
            if (descriptor != null)
            {
                return descriptor.DefaultValue;
            }

            const int DefaultValue = 0;
            return DefaultValue;
        }

        #endregion

        /// <summary>
        /// Handles the KeyDown event of the CleanCodeSettings control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.KeyEventArgs"/> instance containing the event data.</param>
        private void CleanCodeSettings_KeyDown(object sender, KeyEventArgs e)
        {
            // instead of having a TextChanged event on every textbox and input control, we
            // just set the dirty flag when a keyboard stroke was pressed. It is very unlikely
            // that someone copy/paste's only with the mouse for just a few digits.
            this.dirty = true;
            this.tabControl.DirtyChanged();
        }
    }
}
